甘特圖在眾多套件中也有不少已經有提供了,所以除非必要特殊客製化功能,不然應該不需要自己刻一個。
根據圖上需要的部分,分成以下幾個部分。
Axis比例尺
甘特圖的方塊以及顏色識別
甘特圖需要具備的資料是別
時間
Task名稱
console.clear();
const width = 800;
const height = 600;
const padding = 50;
const innerWidth = width - padding * 2;
const innerHeight = height - padding * 2;
const svg = d3.select('svg').attr('width', width).attr('height', height);
const rootLayer = svg.append('g').attr('transform', `translate(${padding}, ${padding})`);
const axisLayer = rootLayer.append('g');
const xAxisLayer = axisLayer.append('g');
const yAxisLayer = axisLayer.append('g');
const tasksLayer = rootLayer.append('g');
let series = [];
let xExtent;
let yExtent;
let xScale;
let yScale;
let xAxis;
let yAxis;
const datas = [
{
"name": "Task1",
"start": "2020/10/09 01:00:00",
"end": "2020/10/09 02:00:00",
"fill": "#b3e2cd"
},
{
"name": "Task2",
"start": "2020/10/09 02:00:00",
"end": "2020/10/09 03:00:00",
"fill": "#fdcdac"
},
{
"name": "Task3",
"start": "2020/10/09 02:00:00",
"end": "2020/10/09 04:00:00",
"fill": "#cbd5e8"
},
{
"name": "Task4",
"start": "2020/10/09 03:00:00",
"end": "2020/10/09 04:00:00",
"fill": "#f4cae4"
},
{
"name": "Task5",
"start": "2020/10/09 03:00:00",
"end": "2020/10/09 05:00:00",
"fill": "#e6f5c9"
}
];
以上皆為基本圖層設定以及基本資料配置。series
為內部使用資料,datas
為原始資料,差異為會將datas
的資料時間格式轉換為時間字串。
const preProcess = () => {
series = datas.map(data => ({
name: data.name,
start: new Date(data.start),
end: new Date(data.end),
fill: data.fill
}))
}
const calcExtent = () => {
xExtent = [d3.min(series, serie => serie.start), d3.max(series, serie => serie.end)];
}
const calcScale = () => {
xScale = d3.scaleTime().range([0, innerWidth]).domain(xExtent);
// 此處使用`ScaleBand`因為是以資料的個別名稱為主,以名稱為類別。
yScale = d3.scaleBand().range([0, innerHeight]).domain(series.map(serie => serie.name));
}
const paintAxis = () => {
xAxis = d3.axisBottom().scale(xScale);
yAxis = d3.axisLeft().scale(yScale);
yAxisLayer.call(yAxis);
xAxisLayer.call(xAxis);
}
const paintTasks = () => {
tasksLayer.selectAll('rect')
.data(series)
.enter()
.append('rect')
.attr('x', serie => xScale(serie.start))
// yScale.bandwidth可以得知,全部種類分別的y軸長度為多少。
.attr('y', serie => yScale(serie.name) + yScale.bandwidth() * 0.25)
// 透過算出頭尾位置,計算出寬度
.attr('width', serie => xScale(serie.end) - xScale(serie.start))
// 為了讓各個Task中間有空格,因此高度只填滿0.5
.attr('height', yScale.bandwidth() * 0.5)
.attr('fill', serie => serie.fill)
}
// 前置資料處理
preProcess();
// 計算Extent
calcExtent();
// 計算Scale
calcScale();
// 繪製比例尺
paintAxis();
// 繪製Task
paintTasks();
甘特圖其實自己實作非常容易,而且擴充方便,可以針對自身特別業務進行擴充,讓甘特圖有趣又好用!